package com.nage.graphics;

import java.util.ArrayList;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.nage.components.display.Drawable;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.util.Log;

public class RenderSystem implements GLSurfaceView.Renderer {

	/** 
	 * view[2] = screen width. view[3] = screen height
	 */
	public static int[] view = new int[4]; // holds the width and hieght of screen in index 2,3
	public static boolean ready = false;
	public static int[] mTextureNames = new int[1];
	Context mContext;
	
	static ArrayList<Drawable> m_Drawables;
	
    // Specifies the format our textures should be converted to upon load.
    public static BitmapFactory.Options sBitmapOptions
        = new BitmapFactory.Options();

	public RenderSystem(Context c) {
		view[0] = 0;
		view[1] = 1;
		mContext = c;
		m_Drawables = new ArrayList<Drawable>();
        sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;

	}
	
	public static void registerDrawable(Drawable d) {
		m_Drawables.add(d);
	}
	
	public static void unregisterDrawable(Drawable d) {
		m_Drawables.remove(d);
	}
	
	public static void unregisterAllDrawables() {
		m_Drawables.clear();
	}

	@Override
	public void onDrawFrame(GL10 gl) {
		executeRenderCommands(gl);
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int w, int h) {
		Log.d("RenderSystem", "onSurfaceChanged() called. w ="+w+" h = "+h);
		view[2] = w;
		view[3] = h;

		if (h == 0) { // Prevent A Divide By Zero By
			view[3] = 1; // Making Height Equal One
		}
		ready = true; 

		// 0, 0, w, h
		gl.glViewport(view[0], view[1], view[2], view[3]);
		gl.glMatrixMode(GL10.GL_PROJECTION); // Select The Projection Matrix
		gl.glLoadIdentity(); // Reset The Projection Matrix

		gl.glOrthof(0, view[2], 0, view[3], -1.0f, 1.0f);

		// Calculate The Aspect Ratio Of The Window
		// GLU.gluPerspective(gl, 45.0f, (float)w / (float)h, 0.1f, 100.0f);

		gl.glMatrixMode(GL10.GL_MODELVIEW); // Select The Modelview Matrix
		gl.glLoadIdentity(); // Reset The Modelview Matrix
		
		loadObjects(gl);
		executeRenderCommands(gl);
		
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		gl.glShadeModel(GL10.GL_SMOOTH); // Enable Smooth Shading
		gl.glClearDepthf(1.0f); // Depth Buffer Setup
		gl.glEnable(GL10.GL_DEPTH_TEST); // Enables Depth Testing
        gl.glEnable(GL10.GL_TEXTURE_2D);
		gl.glDepthFunc(GL10.GL_LEQUAL); // The Type Of Depth Testing To Do

		// Really Nice Perspective Calculations
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
	}


	private synchronized void executeRenderCommands(GL10 gl) {
		// takes a DrawList and traverses it to create the render commands
		// that are required to render that draw list.
		//Log.v("RenderSystem", "Rendering " + toDraw.tag);

		// Clear Screen And Depth Buffer
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity(); // Reset The Current Modelview Matrix

		for (Drawable d : m_Drawables) {
			d.render(gl);
		}
		

	}

	public void loadObjects(GL10 gl) {
		// Once the OpenGL surface has been initialised correctly,
		// the vertex values for any objects on the screen can be
		// calculated.
		try {
			for(Drawable d : m_Drawables) {
				d.calcVertices(gl, mContext);
			}
		} catch (NullPointerException e) {
			Log.e("RenderSystem",
					"Cannot load game objects, GameSimulationSystem unitialised");
		}
	}

}
